package br.usp.ime.dojo.client;

import java.util.ArrayList;
import java.util.List;

import com.google.gwt.core.client.GWT;
import com.google.gwt.user.client.Timer;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.CheckBox;
import com.google.gwt.user.client.ui.ClickListener;
import com.google.gwt.user.client.ui.Composite;
import com.google.gwt.user.client.ui.HorizontalPanel;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.ListBox;
import com.google.gwt.user.client.ui.TextArea;
import com.google.gwt.user.client.ui.VerticalPanel;
import com.google.gwt.user.client.ui.Widget;

public class RoomDisplay extends Composite implements StarterSessionListener {

	private VerticalPanel vPanel_left;
	private VerticalPanel vPanel_right;	
	private HorizontalPanel hPanel;
	private Chat chat;
	private Label descriptionLabel;
	private StarterSession session;
	private TextArea descriptionArea;
	private ListBox languageArea;
	private Label setDescriptionLabel;
	private Label languageLabel;
	private Label setLanguageLabel;
	private Button confirm;
	private Label nameErrorLBL;
	private List<CheckBox> languagesSelection;

	private Button exitRoomBTN;
	private boolean langError;
	private boolean isStarted;

	// TODO - Criar uma classe para coding area
	private Label codeAreaLBL;
	private TextArea codeAreaTA;

	private DojoRoomServiceAsync dojoRoomServiceAsync;
	private PilotListWidget pilotListWidget;
	private ChatServiceAsync chatServiceAsync;

	private UserListWidget userListWidget;
	private DojoRoomDTO dojoRoomDTO;
	private UserDTO userDTO;
	private final int REFRESH_TIME = 1000;
	Timer time;

	private ExitARoomListener exitARoomListener;

	public RoomDisplay(DojoRoomDTO room, UserDTO loggedUser) {
		dojoRoomDTO = room;
		userDTO = loggedUser;
		isStarted = false;

		vPanel_left = new VerticalPanel();
		vPanel_right = new VerticalPanel();		
		hPanel = new HorizontalPanel();

		dojoRoomServiceAsync = GWT.create(DojoRoomService.class);
		chatServiceAsync = GWT.create(ChatService.class);

		createCodingArea();
		createLanguagesList();		
		createExitRoomButton();

		hPanel.setWidth("90%");
		vPanel_left.setWidth("90%");
		vPanel_right.setWidth("10%");

		dojoRoomServiceAsync.join(dojoRoomDTO, userDTO, onJoinCallback());

		createUpdateRoomTimer();

		if(room.getCreator().getName().equals(loggedUser.getName())) {
			langError = false;
			session = new StarterSession();
			session.setStarterSessionListener(this);					
			vPanel_left.add(session);
		}

		hPanel.add(exitRoomBTN);
		hPanel.add(vPanel_left);
		hPanel.add(vPanel_right);
		initWidget(hPanel);
	}

	private void createLanguagesList() {
		AsyncCallback< List<String>> callback = new AsyncCallback<List<String>>() {
			public void onFailure(Throwable caught) {
			}
			public void onSuccess(List<String> result) {
				languagesSelection = new ArrayList<CheckBox>();
				for(String s : result){
					CheckBox cb = new CheckBox(s);
					languagesSelection.add(cb);
					cb.setChecked(true);
					if(!userDTO.getName().equals(dojoRoomDTO.getCreator().getName()))
						cb.setEnabled(false);
					else{
						cb.addClickListener(checkBoxListener());
					}
					vPanel_left.add(cb);
				}

			}
		};		
		dojoRoomServiceAsync.getSelectedLanguages(dojoRoomDTO.getName(), callback);
	}

	private ClickListener checkBoxListener() {
		return new ClickListener(){
			public void onClick(Widget sender) {
				AsyncCallback< List<String> > callback = new AsyncCallback<List<String>>() {
					public void onFailure(Throwable caught) {
					}
					public void onSuccess(List<String> result) {
					}
				};		
				dojoRoomServiceAsync.setSelectedLanguages(dojoRoomDTO.getName(), ((CheckBox)sender).getText(), callback);
			}
		};
	}

	private void createUpdateRoomTimer() {
		Timer time = new Timer(){
			public void run(){
				if(isStarted == false) {
					if (!userDTO.getName().equals(dojoRoomDTO.getCreator().getName())) {
						updateLanguagesSelection();
					}
					verifyIfStartedSession();
				}
				else {
					updateCodingArea();
				}
			}

		};

		time.scheduleRepeating(REFRESH_TIME);
	}

	private AsyncCallback<Void> onJoinCallback() { 
		return new AsyncCallback<Void> (){
			public void onFailure(Throwable caught) {
				descriptionLabel = new Label();
				descriptionLabel.setText(caught.getMessage());
				vPanel_left.add(descriptionLabel);
			}
			public void onSuccess(Void nul) {
				descriptionLabel = new Label();
				languageLabel = new Label();
				descriptionLabel.setText("DESCRIPTION: "+ dojoRoomDTO.getDescription());
				languageLabel.setText("LANGUAGE: " + dojoRoomDTO.getLanguage());
				descriptionLabel.addStyleName("descricao");
				vPanel_left.add(descriptionLabel);
				vPanel_left.add(languageLabel);

				// TODO - Criar uma classe para coding area
				//vPanel_left.add(codeAreaLBL);
				//vPanel_left.add(codeAreaTA);

				chat = new Chat(userDTO.getName(), dojoRoomDTO.getName());
				vPanel_left.add(chat);		

				pilotListWidget = new PilotListWidget(userDTO,dojoRoomDTO);
				vPanel_right.add(pilotListWidget);

				userListWidget = new UserListWidget(dojoRoomDTO.getName());
				vPanel_right.add(userListWidget);
			}
		};
	}

	private void createExitRoomButton() {
		exitRoomBTN = new Button("Exit");
		exitRoomBTN.addClickListener(new ClickListener(){
			public void onClick(Widget sender) {
				exitDojoRoom(userDTO.getName(), dojoRoomDTO.getName());
				exitChat(userDTO.getName(), dojoRoomDTO.getName());
				//we have to find some bug in the chat after exiting and reentering a room
				chatServiceAsync = null;
				exitARoomListener.onExitDojoRoom();
			}
		});
	}

	private void createCodingArea() {
		// TODO - Classe para coding area
		codeAreaLBL = new Label("Code Area");
		codeAreaTA = new TextArea();
		codeAreaTA.setReadOnly(true);
		codeAreaTA.setVisibleLines(15);
		codeAreaTA.setWidth("100%");
	}

	private void updateLanguagesSelection() {
		AsyncCallback< List<String> > callback = new AsyncCallback<List<String> >() {

			public void onFailure(Throwable caught) {

			}

			public void onSuccess(List<String> result) {
				if(languagesSelection != null){
					for(CheckBox cb : languagesSelection){
						if(result.contains(cb.getText())){
							cb.setChecked(true);
						}
						else cb.setChecked(false);

					}
				}
			}
		};		
		dojoRoomServiceAsync.getSelectedLanguages(dojoRoomDTO.getName(),  callback);

	}


	public void verifyIfStartedSession() {
		AsyncCallback< Boolean > callback = new AsyncCallback< Boolean >() {

			public void onFailure(Throwable caught) {
			}

			public void onSuccess(Boolean result) {
				if(result) {
					startSession();
					vPanel_left.remove(chat);
					vPanel_left.add(codeAreaLBL);
					vPanel_left.add(codeAreaTA);
					isStarted = true;
					vPanel_left.add(chat);
				}				
			}
		};		
		dojoRoomServiceAsync.verifyIfIsStarted(dojoRoomDTO.getName(), callback);
	}

	public void startSession() {
		AsyncCallback< ArrayList<String> > callback = new AsyncCallback< ArrayList<String> >() {

			public void onFailure(Throwable caught) {
			}

			public void onSuccess(ArrayList<String> result) {
				dojoRoomDTO.setDescription(result.get(0)); // refactoring needed. what is the DojoRoomDTO for?...
				dojoRoomDTO.setLanguage(result.get(1));
				languageLabel.setText("LANGUAGE: " + dojoRoomDTO.getLanguage());
				descriptionLabel.setText("DESCRIPTION: "+ dojoRoomDTO.getDescription());
				dojoRoomDTO.setStarted(true);
			}
		};		
		dojoRoomServiceAsync.updateRoomDescriptionInClient(dojoRoomDTO.getName(), callback);
	}

	public void onClickStartSuccess() {
		vPanel_left.remove(session);
		descriptionArea = new TextArea();
		descriptionArea.setText(dojoRoomDTO.getDescription());
		languageArea = new ListBox();
		languageArea.addItem("NONE");
		languageArea.addItem("C++");
		languageArea.addItem("Java");
		languageArea.addItem("C");
		languageArea.addItem("Pascal");
		languageArea.addItem("Ruby");
		languageArea.addItem("Python");
		languageArea.addItem("SmallTalk");
		languageArea.addItem("Perl");
		confirm = new Button("confirm");
		confirm.addClickListener(new ClickListener(){
			public void onClick(Widget sender) {
				updateRoomDescription();
			}			
		});

		setDescriptionLabel = new Label("Set the description of the started room");
		setLanguageLabel = new Label("Set the language of the started room");
		vPanel_left.remove(chat);
		vPanel_left.add(setDescriptionLabel);
		vPanel_left.add(descriptionArea);
		descriptionArea.setWidth("100%");
		vPanel_left.add(setLanguageLabel);
		vPanel_left.add(languageArea);
		vPanel_left.add(confirm);
		vPanel_left.add(chat);
	}


	public void updateRoomDescription() {

		AsyncCallback<Boolean> callback = new AsyncCallback<Boolean>() {

			/* I did't understand how to get a failure here, cause the UpdateRoomDescriptionImpl
			 * is getting an Exception when pilot queue has less than 2 users, and
			 * it continue going to onSuccess. So i made a "gambiarra".
			 * if someone knows how to do this correctly!
			 */
			public void onFailure(Throwable caught) {
				setNameError("Pilot Queue contains less than 2 users!!");
			}

			public void onSuccess(Boolean result) {
				if(result == false) {
					setNameError("Pilot Queue contains less than 2 users!!");
				}
				else {
					vPanel_left.remove(descriptionArea);
					vPanel_left.remove(languageArea);
					vPanel_left.remove(confirm);
					vPanel_left.remove(session);				
					vPanel_left.remove(setLanguageLabel);
					vPanel_left.remove(setDescriptionLabel);
					if(langError) {
						vPanel_left.remove(nameErrorLBL);
					}
				}
			}
		};


		if(languageArea.getSelectedIndex() != 0 && !descriptionArea.getText().equals("")) 
			dojoRoomServiceAsync.updateRoomDescriptionInServer(descriptionArea.getText(), languageArea.getItemText(languageArea.getSelectedIndex()), dojoRoomDTO.getName(), callback);
		else {
			setNameError("You should select the Language");
		}

		if(descriptionArea.getText().equals("")) {
			setNameError("Description is empty!");
		}

	}

	private void setNameError(String string) {
		if(langError == false) {
			nameErrorLBL = new Label(string);
			nameErrorLBL.setStylePrimaryName("dojoError");
			vPanel_left.remove(chat);
			vPanel_left.add((nameErrorLBL));
			vPanel_left.add(chat);
			langError = true;
		}
		else nameErrorLBL.setText(string);
	}



	public void updateCodingArea() {
		if(userDTO.getName().equals(pilotListWidget.getPilot())) {
			codeAreaTA.setReadOnly(false);
			sendCodeToServer();
		}

		else {
			/*if(userDTO.getName().equals(userListWidget.getCopilot())) {
		    	codeAreaTA.setReadOnly(false);
		    }*/
			uptadeCodeToClient();
		}		
	}

	private void sendCodeToServer() {
		AsyncCallback< Void > callback = new AsyncCallback< Void >() {
			public void onFailure(Throwable caught) {
			}
			public void onSuccess(Void result) {								
			}
		};		
		dojoRoomServiceAsync.updateCodeInServer(codeAreaTA.getText(), callback);		
	}

	private void uptadeCodeToClient() {
		AsyncCallback< String > callback = new AsyncCallback< String >() {
			public void onFailure(Throwable caught) {
			}
			public void onSuccess(String result) {
				//The same problem we got in the CHAT, scroll keeps moving!
				//Scroll Bar, or some other way.
				codeAreaTA.setText(result);
			}
		};		
		dojoRoomServiceAsync.updateCodeInClient(callback);
	}

	private void exitDojoRoom(String userName, String dojoRoomName) {
		AsyncCallback< Void> callback = new AsyncCallback<Void>() {
			public void onFailure(Throwable caught) {
			}
			public void onSuccess(Void result) {
			}
		};		
		dojoRoomServiceAsync.exitDojoRoom(userName, dojoRoomName, callback);
	}

	private void exitChat(String userName, String dojoRoomName) {
		AsyncCallback< Void> callback = new AsyncCallback<Void>() {
			public void onFailure(Throwable caught) {
			}
			public void onSuccess(Void result) {
			}
		};		
		chatServiceAsync.unregisterUser(userName, dojoRoomName, callback);
	}


	public void setExitADojoRoomListener(ExitARoomListener listener) {
		exitARoomListener = listener;
	}

}
